﻿
using Xacml.Core.Context;
using Xacml.Core.Interfaces;
using Xacml.Core.Policy;
using System.Collections;

namespace Xacml.Core.Runtime
{
  public abstract class TargetItems
  {
    private TargetEvaluationValue _evaluationValue;
    private TargetItemsBaseReadWrite _targetItems;

    protected TargetItems(TargetItemsBaseReadWrite targetItems)
    {
      this._targetItems = targetItems;
    }

    public virtual TargetEvaluationValue Evaluate(EvaluationContext context, Xacml.Core.Context.TargetItemBase targetItem)
    {
      if (this._targetItems.IsAny)
      {
        context.Trace("IsAny");
        return TargetEvaluationValue.Match;
      }
      this._evaluationValue = TargetEvaluationValue.NoMatch;
      foreach (TargetItemBaseReadWrite itemBaseReadWrite in (CollectionBase) this._targetItems.ItemsList)
      {
        foreach (TargetMatchBase match in (CollectionBase) itemBaseReadWrite.Match)
        {
          this._evaluationValue = TargetEvaluationValue.NoMatch;
          context.Trace("Using function: {0}", (object) match.MatchId);
          IFunction function = context.Engine.GetFunction(match.MatchId);
          if (function == null)
          {
            context.Trace("ERR: function not found {0}", (object) match.MatchId);
            context.ProcessingError = true;
            return TargetEvaluationValue.Indeterminate;
          }
          if (function.Returns == null)
          {
            context.Trace("ERR: The function '{0}' does not defines it's return value", (object) match.MatchId);
            context.ProcessingError = true;
            return TargetEvaluationValue.Indeterminate;
          }
          if (function.Returns != DataTypeDescriptor.Boolean)
          {
            context.Trace("ERR: Function does not return Boolean a value");
            context.ProcessingError = true;
            return TargetEvaluationValue.Indeterminate;
          }
          AttributeElement attributeElement = context.Engine.Resolve(context, match, targetItem);
          if (attributeElement != null)
          {
            context.Trace("Attribute found, evaluating match function");
            try
            {
              this._evaluationValue = context.Engine.EvaluateFunction(context, function, (IFunctionParameter) match.AttributeValue, (IFunctionParameter) attributeElement).BoolValue ? TargetEvaluationValue.Match : TargetEvaluationValue.NoMatch;
            }
            catch (EvaluationException ex)
            {
              context.Trace("ERR: {0}", (object) ex.Message);
              this._evaluationValue = TargetEvaluationValue.Indeterminate;
            }
          }
          if (match.AttributeReference.MustBePresent && context.IsMissingAttribute)
          {
            context.Trace("Attribute not found and must be present");
            this._evaluationValue = TargetEvaluationValue.Indeterminate;
          }
          if (this._evaluationValue != TargetEvaluationValue.Match)
            break;
        }
        if (this._evaluationValue == TargetEvaluationValue.Match)
          return this._evaluationValue;
      }
      return this._evaluationValue;
    }
  }
}
